package app

import (
	"context"
	"sync"

	"gitee.com/mkwchecking/sr-core/config/source"
	"gitee.com/mkwchecking/sr-core/domain"
	"gitee.com/mkwchecking/sr-core/relay"
	"gitee.com/mkwchecking/sr-core/yerr"
	"github.com/go-kratos/kratos/v2/transport"
)

func New(opts ...Option) IApp {
	appinfo := defaultAppInfo()
	for _, opt := range opts {
		opt(appinfo)
	}

	relay.MixToContext(func(ctx context.Context) context.Context {
		return appinfo.ctx
	})

	return &YApp{
		appinfo: appinfo,
		lock:    sync.Mutex{},
		source:  appinfo.source,
		domains: make([]domain.IDomain, 0),
	}

}

type YApp struct {
	appinfo *AppInfo

	lock    sync.Mutex
	domains []domain.IDomain

	source   source.ISource
	_didInit bool

	servers []transport.Server
}

func (y *YApp) Install(ctx context.Context, force bool) error {
	y.lock.Lock()
	defer y.lock.Unlock()

	var err error
	if force || !y._didInit {
		defer func() {
			y._didInit = err == nil
		}()
		err = y.installModules(ctx)
	} else {
		err = yerr.ErrDataRepeat.Error("module did install ,do you wang to [force] reinstall ?")
	}

	return err
}

func (y *YApp) Source() source.ISource {
	return y.source
}

func (y *YApp) installModules(ctx context.Context) error {
	for _, d := range y.domains {
		if err := d.Install(ctx); err != nil {
			return err
		}
	}
	return nil
}

func (y *YApp) Meta() map[string]string {
	return y.appinfo.meta
}

func (y *YApp) Context() context.Context {
	return y.appinfo.ctx
}

func (y *YApp) AddServer(srv transport.Server) error {
	y.lock.Lock()
	defer y.lock.Unlock()

	y.servers = append(y.servers, srv)
	return nil
}

func (y *YApp) beforeStart() error {
	for _, d := range y.domains {
		if err := d.BeforeStart(y.source); err != nil {
			return err
		}
	}
	return nil
}

// Start implements IApp.
func (y *YApp) Start(ctx context.Context) error {
	if err := y.beforeStart(); err != nil {
		return err
	}

	for _, m := range y.domains {
		if err := m.Start(ctx); err != nil {
			return err
		}
	}

	for _, s := range y.servers {
		if err := s.Start(ctx); err != nil {
			return err
		}
	}
	return nil
}

// Stop implements IApp.
func (y *YApp) Stop(ctx context.Context) error {
	for _, m := range y.domains {
		if err := m.Stop(ctx); err != nil {
			return err
		}
	}
	return nil
}

// WithModule implements IApp.
func (y *YApp) AddDomain(domain domain.IDomain) IApp {
	y.lock.Lock()
	defer y.lock.Unlock()

	y.domains = append(y.domains, domain)
	return y
}

func (y *YApp) Domains() []domain.IDomain {
	return y.domains
}
